Skip to content

feat(cloud-agent-next): enforce container billing - #5358

Open
pandemicsyn wants to merge 7 commits into
mainfrom
feat/cloud-agent-paid-container-billing
Open

feat(cloud-agent-next): enforce container billing#5358
pandemicsyn wants to merge 7 commits into
mainfrom
feat/cloud-agent-paid-container-billing

Conversation

@pandemicsyn

Copy link
Copy Markdown
Contributor

Summary

  • add paid start admission and balance details to the container-usage RPC while preserving the existing start contract
  • gate Cloud Agent personal and organization payers independently at both producer and meter layers
  • durably block shared sandboxes on budget stop, reject queued/new/reconnecting work, gracefully drain wrappers, and force-destroy after 120 seconds
  • require a new successful meter admission before blocked sandboxes can resume
  • make Cloud Agent session payer organization immutable

Rollout

  • all Cloud Agent producer flags default to disabled and all meter Cloud Agent payer lists default to empty
  • deploy the container-usage meter with recordStartV2 before enabling any Cloud Agent payer flags
  • Cloud Agent service names are intentionally not added to CONTAINER_BILLING_SERVICES in this PR

Verification

  • pnpm test and pnpm typecheck in packages/container-usage
  • pnpm test, pnpm typecheck, and pnpm lint in services/container-usage-meter
  • pnpm test:postgres in services/container-usage-meter
  • pnpm test, pnpm typecheck, pnpm lint, and pnpm test:wrapper in services/cloud-agent-next
  • pnpm test, pnpm typecheck, and pnpm lint in services/session-ingest
  • git diff --cached --check before commit

@pandemicsyn
pandemicsyn marked this pull request as ready for review August 19, 2026 17:12
? await this.getPendingStopReason(context.generation)
: undefined;
await super.destroy();
if (context) await updateBillingContext(this.ctx.storage, context);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: destroy() restores a pre-super.destroy() billing snapshot and can undo concurrent settlement.

This restore is not serialized with the heartbeat controller lock. While super.destroy() is awaited, a heartbeat can advance usageMeasuredAtMs, after which writing the captured context back rewinds already-billed seconds and double-counts them on the later physical recordStop. Re-read storage after destroy and restore only keys that are still missing, or skip the write when a newer measurement or settlement is already present.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

try {
// The control plane may fail after the deadline. Keep the durable block and
// reissue destroy until the physical stop hook settles the generation.
await this.destroy();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Successful destroy() is never reissued, so a no-throw control-plane miss never settles.

The comment says to keep issuing destroy until onStop settles the generation, but the retry is only in the catch path. scheduleForceStop already deleted other billingForceStop alarms, and this restore does not re-arm a heartbeat. If destroy() returns and onStop is dropped or delayed, the generation stays open with no 5s retry and no 120s killer. Re-arm billingForceStop until the generation is gone or onStop has closed it.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

const metadata = await this.getMetadata();
if (!metadata || !isCloudAgentContainerBillingEnabled(this.env, metadata.identity))
return false;
return createAgentSandbox(this.env, metadata).isBillingBlocked();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Reconnect billing checks fail open when the sandbox RPC errors.

isContainerBillingBlocked() already established that enforcement is enabled, then calls isBillingBlocked() with no enforcement bit. isSandboxBillingBlocked() therefore returns false on a throw, so ingest reconnect accepts the socket and in-flight work continues. The facade path in this PR passes enforcementRequested and then requires admission. Treat a failed probe as blocked, or call ensureBillingAdmission() here too.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Executive Summary

Force-destroy restore can rewind billed time or leave a paid generation unclosed, and reconnect billing checks fail open when the sandbox RPC errors.

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
services/cloud-agent-next/src/container-usage.ts 507 destroy() restores a pre-super.destroy() snapshot and can undo concurrent settlement
services/cloud-agent-next/src/container-usage.ts 700 Successful destroy() is never reissued if onStop never arrives
services/cloud-agent-next/src/persistence/CloudAgentSession.ts 775 Reconnect billing checks fail open when the sandbox RPC errors
Files Reviewed (6 files)
  • services/cloud-agent-next/src/kilo-facade/session-proxy.ts
  • services/cloud-agent-next/src/kilo-facade/session-proxy.test.ts
  • services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts
  • services/cloud-agent-next/src/kilo-facade/user-kilo-facade.test.ts
  • services/container-usage-meter/src/billing-config.ts
  • services/container-usage-meter/src/billing-config.test.ts

Fix these issues in Kilo Cloud

Previous Review Summary (commit 77f42f7)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 77f42f7)

Status: 3 Issues Found | Recommendation: Address before merge

Executive Summary

Force-destroy restore can rewind billed time or leave a paid generation unclosed, and reconnect billing checks fail open when the sandbox RPC errors.

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
services/cloud-agent-next/src/container-usage.ts 507 destroy() restores a pre-super.destroy() snapshot and can undo concurrent settlement
services/cloud-agent-next/src/container-usage.ts 700 Successful destroy() is never reissued if onStop never arrives
services/cloud-agent-next/src/persistence/CloudAgentSession.ts 775 Reconnect billing checks fail open when the sandbox RPC errors
Files Reviewed (42 files)
  • .specs/gastown-usage-based-billing.md
  • packages/container-usage/src/client.test.ts
  • packages/container-usage/src/client.ts
  • packages/container-usage/src/contracts.test.ts
  • packages/container-usage/src/contracts.ts
  • packages/container-usage/src/heartbeat.test.ts
  • packages/container-usage/src/heartbeat.ts
  • services/cloud-agent-next/src/agent-sandbox/cloudflare/cloudflare-agent-sandbox.test.ts
  • services/cloud-agent-next/src/agent-sandbox/cloudflare/cloudflare-agent-sandbox.ts
  • services/cloud-agent-next/src/agent-sandbox/protocol.ts
  • services/cloud-agent-next/src/container-billing-rollout.test.ts
  • services/cloud-agent-next/src/container-billing-rollout.ts
  • services/cloud-agent-next/src/container-usage-context.ts
  • services/cloud-agent-next/src/container-usage.test.ts - 0 issues
  • services/cloud-agent-next/src/container-usage.ts - 2 issues
  • services/cloud-agent-next/src/execution/types.ts
  • services/cloud-agent-next/src/kilo-facade/session-proxy.test.ts
  • services/cloud-agent-next/src/kilo-facade/session-proxy.ts
  • services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts
  • services/cloud-agent-next/src/persistence/CloudAgentSession.ts - 1 issue
  • services/cloud-agent-next/src/session/agent-runtime.test.ts
  • services/cloud-agent-next/src/session/agent-runtime.ts
  • services/cloud-agent-next/src/session/pending-messages.ts
  • services/cloud-agent-next/src/session/queue-message.ts
  • services/cloud-agent-next/src/session/session-message-queue.test.ts
  • services/cloud-agent-next/src/session/session-message-queue.ts
  • services/cloud-agent-next/src/terminal/access.test.ts
  • services/cloud-agent-next/src/types.ts
  • services/cloud-agent-next/src/websocket/ingest.test.ts
  • services/cloud-agent-next/src/websocket/ingest.ts
  • services/cloud-agent-next/worker-configuration.d.ts
  • services/cloud-agent-next/wrangler.jsonc
  • services/cloud-agent-next/wrapper/src/lifecycle.test.ts
  • services/cloud-agent-next/wrapper/src/lifecycle.ts
  • services/cloud-agent-next/wrapper/src/main.ts
  • services/cloud-agent-next/wrapper/src/shutdown.test.ts
  • services/cloud-agent-next/wrapper/src/shutdown.ts
  • services/container-usage-meter/src/billing-config.test.ts
  • services/container-usage-meter/src/billing-config.ts
  • services/container-usage-meter/src/meter.test.ts
  • services/container-usage-meter/src/meter.ts
  • services/container-usage-meter/src/postgres.ts
  • services/container-usage-meter/test/postgres.test.ts
  • services/container-usage-meter/worker-configuration.d.ts
  • services/container-usage-meter/wrangler.jsonc
  • services/session-ingest/src/session-ingest-rpc.test.ts
  • services/session-ingest/src/session-ingest-rpc.ts

Fix these issues in Kilo Cloud


Reviewed by grok-4.6 · Input: 87.9K · Output: 21K · Cached: 521.7K

Review guidance: REVIEW.md from base branch main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant